<!-- uniapp vue3 markdown解析 -->
<template>
  <text :selectable="true" class="ua__markdown_text">
    <view class="ua__markdown">
      <rich-text space="nbsp" :nodes="parseNodes(source)" @itemclick="handleItemClick"></rich-text>
    </view>
  </text>
</template>

<script setup>
  import { ref, computed } from 'vue';
  import MarkdownIt from './lib/markdown-it.min.js';
  import hljs from './lib/highlight/uni-highlight.min.js';
  import './lib/highlight/atom-one-dark.css';
  import parseHtml from './lib/html-parser.js';
  const props = defineProps({
    // 解析内容
    source: String,
    showLine: { type: [Boolean, String], default: true }
  });

  let copyCodeData = [];
  const markdown = MarkdownIt({
    html: true,
    highlight: function (str, lang) {
      let preCode = '';
      try {
        preCode = hljs.highlightAuto(str).value;
      } catch (err) {
        preCode = markdown.utils.escapeHtml(str);
      }
      const lines = preCode.split(/\n/).slice(0, -1);
      // 添加自定义行号
      let html = lines
        .map((item, index) => {
          if (item == '') {
            return '';
          }
          return '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' + item + '</li>';
        })
        .join('');
      if (props.showLine) {
        html = '<ol style="padding: 0px 30px;">' + html + '</ol>';
      } else {
        html = '<ol style="padding: 0px 7px;list-style:none;">' + html + '</ol>';
      }
      copyCodeData.push(str);
      let htmlCode = `<div class="markdown-wrap">`;
      // #ifndef MP-WEIXIN
      htmlCode += `<div style="color: #aaa;text-align: right;font-size: 12px;padding:8px;">`;
      htmlCode += `${lang}<a class="copy-btn" code-data-index="${copyCodeData.length - 1}" style="margin-left: 8px;">复制代码</a>`;
      htmlCode += `</div>`;
      // #endif
      htmlCode += `<pre class="hljs" style="padding:10px 8px 0;margin-bottom:5px;overflow: auto;display: block;border-radius: 5px;"><code>${html}</code></pre>`;
      htmlCode += '</div>';
      return htmlCode;
    }
  });
  const parseNodes = value => {
    if (!value) return;
    // 解析<br />到\n
    value = value.replace(/<br>|<br\/>|<br \/>/g, '\n');
    value = value.replace(/&nbsp;/g, ' ');
    let htmlString = '';
    if (value.split('```').length % 2) {
      let mdtext = value;
      if (mdtext[mdtext.length - 1] != '\n') {
        mdtext += '\n';
      }
      htmlString = markdown.render(mdtext);
    } else {
      htmlString = markdown.render(value);
    }
    // 解决小程序表格边框型失效问题
    htmlString = htmlString.replace(/<table/g, `<table class="table"`);
    htmlString = htmlString.replace(/<tr/g, `<tr class="tr"`);
    htmlString = htmlString.replace(/<th>/g, `<th class="th">`);
    htmlString = htmlString.replace(/<td/g, `<td class="td"`);
    htmlString = htmlString.replace(/<hr>|<hr\/>|<hr \/>/g, `<hr class="hr">`);

    // #ifndef APP-NVUE
    return htmlString;
    // #endif

    // 将htmlString转成htmlArray，反之使用rich-text解析
    // #ifdef APP-NVUE
    return parseHtml(htmlString);
    // #endif
  };

  // 复制代码
  const handleItemClick = e => {
    let { attrs } = e.detail.node;
    let { 'code-data-index': codeDataIndex, class: className } = attrs;
    if (className == 'copy-btn') {
      uni.setClipboardData({
        data: copyCodeData[codeDataIndex],
        showToast: false,
        success() {
          uni.showToast({
            title: '复制成功',
            icon: 'none'
          });
        }
      });
    }
  };
</script>

<style lang="scss" scoped>
  .ua__markdown {
    font-size: 14px;
    line-height: 1.5;
    word-break: break-all;
    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
      font-family: inherit;
      font-weight: 500;
      line-height: 1.1;
      color: inherit;
    }
    h1,
    h2,
    h3 {
      margin-top: 20px;
      margin-bottom: 10px;
    }
    h4,
    h5,
    h6 {
      margin-top: 10px;
      margin-bottom: 10px;
    }
    .h1,
    h1 {
      font-size: 36px;
    }
    .h2,
    h2 {
      font-size: 30px;
    }
    .h3,
    h3 {
      font-size: 24px;
    }
    .h4,
    h4 {
      font-size: 18px;
    }
    .h5,
    h5 {
      font-size: 14px;
    }
    .h6,
    h6 {
      font-size: 12px;
    }
    a {
      background-color: transparent;
      color: #2196f3;
      text-decoration: none;
    }
    hr,
    ::v-deep .hr {
      margin-top: 20px;
      margin-bottom: 20px;
      border: 0;
      border-top: 1px solid #e5e5e5;
    }
    img {
      max-width: 35%;
    }
    p {
      margin: 0 0 10px;
      overflow-wrap: break-word;
      word-break: break-all;
    }
    text {
      overflow-wrap: break-word;
      word-break: break-all;
    }
    em {
      font-style: italic;
      font-weight: inherit;
    }
    ol,
    ul {
      margin-top: 0;
      margin-bottom: 10px;
      padding-left: 40px;
    }
    ol ol,
    ol ul,
    ul ol,
    ul ul {
      margin-bottom: 0;
    }
    ol ol,
    ul ol {
      list-style-type: lower-roman;
    }
    ol ol ol,
    ul ul ol {
      list-style-type: lower-alpha;
    }
    dl {
      margin-top: 0;
      margin-bottom: 20px;
    }
    dt {
      font-weight: 600;
    }
    dt,
    dd {
      line-height: 1.4;
    }
    .task-list-item {
      list-style-type: none;
    }
    .task-list-item input {
      margin: 0 0.2em 0.25em -1.6em;
      vertical-align: middle;
    }
    pre {
      position: relative;
      z-index: 11;
    }
    code,
    kbd,
    pre,
    samp {
      font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
    }
    code:not(.hljs) {
      padding: 2px 4px;
      font-size: 90%;
      color: #c7254e;
      background-color: #ffe7ee;
      border-radius: 4px;
    }
    code:empty {
      display: none;
    }
    pre code.hljs {
      color: var(--vg__text-1);
      border-radius: 16px;
      background: var(--vg__bg-1);
      font-size: 12px;
    }
    .markdown-wrap {
      font-size: 12px;
      margin-bottom: 10px;
    }
    pre.code-block-wrapper {
      background: #2b2b2b;
      color: #f8f8f2;
      border-radius: 4px;
      overflow-x: auto;
      padding: 1em;
      position: relative;
    }
    pre.code-block-wrapper code {
      padding: auto;
      font-size: inherit;
      color: inherit;
      background-color: inherit;
      border-radius: 0;
    }
    .code-block-header__copy {
      font-size: 16px;
      margin-left: 5px;
    }
    abbr[data-original-title],
    abbr[title] {
      cursor: help;
      border-bottom: 1px dotted #777;
    }
    blockquote {
      padding: 10px 20px;
      margin: 0 0 20px;
      font-size: 17.5px;
      border-left: 5px solid #e5e5e5;
    }
    blockquote ol:last-child,
    blockquote p:last-child,
    blockquote ul:last-child {
      margin-bottom: 0;
    }
    blockquote .small,
    blockquote footer,
    blockquote small {
      display: block;
      font-size: 80%;
      line-height: 1.42857143;
      color: #777;
    }
    blockquote .small:before,
    blockquote footer:before,
    blockquote small:before {
      content: '\2014 \00A0';
    }
    .blockquote-reverse,
    blockquote.pull-right {
      padding-right: 15px;
      padding-left: 0;
      text-align: right;
      border-right: 5px solid #eee;
      border-left: 0;
    }
    .blockquote-reverse .small:before,
    .blockquote-reverse footer:before,
    .blockquote-reverse small:before,
    blockquote.pull-right .small:before,
    blockquote.pull-right footer:before,
    blockquote.pull-right small:before {
      content: '';
    }
    .blockquote-reverse .small:after,
    .blockquote-reverse footer:after,
    .blockquote-reverse small:after,
    blockquote.pull-right .small:after,
    blockquote.pull-right footer:after,
    blockquote.pull-right small:after {
      content: '\00A0 \2014';
    }
    .footnotes {
      -moz-column-count: 2;
      -webkit-column-count: 2;
      column-count: 2;
    }
    .footnotes-list {
      padding-left: 2em;
    }
    table,
    ::v-deep .table {
      border-spacing: 0;
      border-collapse: collapse;
      width: 100%;
      max-width: 100%;
      overflow: auto;
      margin-top: 0;
      margin-bottom: 16px;
      display: block;
    }
    table tr,
    ::v-deep .table .tr {
      border-top: 1px solid #e5e5e5;
    }
    table th,
    table td,
    ::v-deep .table .th,
    ::v-deep .table .td {
      padding: 6px 13px;
      border: 1px solid #e5e5e5;
    }
    table th,
    ::v-deep .table .th {
      font-weight: 600;
      background-color: #eee;
    }
    .hljs[class*='language-']:before {
      position: absolute;
      z-index: 3;
      top: 0.8em;
      right: 1em;
      font-size: 0.8em;
      color: #999;
    }
    .hljs[class~='language-js']:before {
      content: 'js';
    }
    .hljs[class~='language-ts']:before {
      content: 'ts';
    }
    .hljs[class~='language-html']:before {
      content: 'html';
    }
    .hljs[class~='language-md']:before {
      content: 'md';
    }
    .hljs[class~='language-vue']:before {
      content: 'vue';
    }
    .hljs[class~='language-css']:before {
      content: 'css';
    }
    .hljs[class~='language-sass']:before {
      content: 'sass';
    }
    .hljs[class~='language-scss']:before {
      content: 'scss';
    }
    .hljs[class~='language-less']:before {
      content: 'less';
    }
    .hljs[class~='language-stylus']:before {
      content: 'stylus';
    }
    .hljs[class~='language-go']:before {
      content: 'go';
    }
    .hljs[class~='language-java']:before {
      content: 'java';
    }
    .hljs[class~='language-c']:before {
      content: 'c';
    }
    .hljs[class~='language-sh']:before {
      content: 'sh';
    }
    .hljs[class~='language-yaml']:before {
      content: 'yaml';
    }
    .hljs[class~='language-py']:before {
      content: 'py';
    }
    .hljs[class~='language-docker']:before {
      content: 'docker';
    }
    .hljs[class~='language-dockerfile']:before {
      content: 'dockerfile';
    }
    .hljs[class~='language-makefile']:before {
      content: 'makefile';
    }
    .hljs[class~='language-javascript']:before {
      content: 'js';
    }
    .hljs[class~='language-typescript']:before {
      content: 'ts';
    }
    .hljs[class~='language-markup']:before {
      content: 'html';
    }
    .hljs[class~='language-markdown']:before {
      content: 'md';
    }
    .hljs[class~='language-json']:before {
      content: 'json';
    }
    .hljs[class~='language-ruby']:before {
      content: 'rb';
    }
    .hljs[class~='language-python']:before {
      content: 'py';
    }
    .hljs[class~='language-bash']:before {
      content: 'sh';
    }
    .hljs[class~='language-php']:before {
      content: 'php';
    }
  }
</style>

<style lang="scss">
  .ua__markdown {
    * {
      word-break: break-all !important;
      overflow-wrap: break-word !important;
    }
  }
  .ua__markdown_text {
    white-space: normal !important;
  }
</style>
